/*
 *    Copyright (c) 2018-2025, lengleng All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the pig4cloud.com developer nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * Author: lengleng (wangiegie@gmail.com)
 */

package com.pig4cloud.pigx.wallet.controller;

import cn.hutool.crypto.symmetric.AES;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.excel.annotation.ResponseExcel;
import com.pig4cloud.pigx.common.log.annotation.SysLog;
import com.pig4cloud.pigx.common.security.annotation.Inner;
import com.pig4cloud.pigx.wallet.api.feign.RemoteRippleUserService;
import com.pig4cloud.pigx.wallet.constant.WalletConstant;
import com.pig4cloud.pigx.wallet.entity.Account;
import com.pig4cloud.pigx.wallet.entity.CapitalAccount;
import com.pig4cloud.pigx.wallet.entity.ContractAccount;
import com.pig4cloud.pigx.wallet.entity.Currency;
import com.pig4cloud.pigx.wallet.mapper.AccountMapper;
import com.pig4cloud.pigx.wallet.service.AccountService;
import com.pig4cloud.pigx.wallet.service.CapitalAccountService;
import com.pig4cloud.pigx.wallet.service.ContractAccountService;
import com.pig4cloud.pigx.wallet.service.CurrencyService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.tron.trident.core.key.KeyPair;
import org.web3j.crypto.ECKeyPair;
import org.web3j.crypto.Keys;
import org.web3j.protocol.Web3j;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.List;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 账户
 *
 * @since 2022-08-09 11:27:36
 */
@RestController
@RequiredArgsConstructor
@RequestMapping("/account")
@Api(value = "account", tags = "账户管理")
public class AccountController {

	private final AccountService accountService;

	private final AES aes;

	private final CapitalAccountService capitalAccountService;

	private final ContractAccountService contractAccountService;

	private final CurrencyService currencyService;

	private final RemoteRippleUserService remoteRippleUserService;

	private final Web3j web3j;

	@Resource(name = "accountListThreadPoolExecutor")
	private ThreadPoolExecutor accountListThreadPoolExecutor;

	private final AccountMapper accountMapper;

	/**
	 * 分页查询
	 *
	 * @param page    分页对象
	 * @param account 账户
	 */
	@ApiOperation(value = "分页查询", notes = "分页查询")
	@GetMapping("/page")
	@PreAuthorize("@pms.hasPermission('wallet_account_view')")
	public R<IPage<Account>> getAccountPage(Page<Account> page, Account account) {
		IPage<Account> accountIPage = accountMapper.selectAccountJoinUser(page, account.getUserId(), account.getAddress());

		Currency ethCurrency = currencyService.getOne(new LambdaQueryWrapper<Currency>()
				.eq(Currency::getPublicChain, WalletConstant.ETH)
				.eq(Currency::getState, WalletConstant.TRUE)
				.eq(Currency::getName, WalletConstant.USDT)
		);

		Currency tronCurrency = currencyService.getOne(new LambdaQueryWrapper<Currency>()
				.eq(Currency::getPublicChain, WalletConstant.TRON)
				.eq(Currency::getState, WalletConstant.TRUE)
				.eq(Currency::getName, WalletConstant.USDT)
		);
		accountIPage.getRecords().forEach(e -> {
			if (e.getMainNetwork().equals(WalletConstant.ETH)) {
				e.setCurrency(ethCurrency.getName());
			}
			if (e.getMainNetwork().equals(WalletConstant.TRON)) {
				e.setCurrency(tronCurrency.getName());
			}
		});

		return R.ok(accountIPage);
//		List<Account> records = iPage.getRecords();
//		List<Long> userIdList = records.stream().map(Account::getUserId).collect(Collectors.toList());
//
//		List<CompletableFuture> completableFutureList = new ArrayList<>();
//
//		CompletableFuture<Void> voidCompletableFuture1 = CompletableFuture.runAsync(() -> {
//			R<List<User>> userList = remoteRippleUserService.getUserList(userIdList, SecurityConstants.FROM_IN);
//			List<User> data = userList.getData();
//			records.forEach(record -> {
//				record.setCurrency(WalletConstant.USDT);
//				data.forEach(item -> {
//					if (record.getUserId().equals(item.getUid())) {
//						record.setEmail(item.getEmail());
//					}
//				});
//			});
//		}, accountListThreadPoolExecutor);
//
//		completableFutureList.add(voidCompletableFuture1);
//
//
//		List<TypeReference<?>> outputParameters = new ArrayList<>();
//		TypeReference<Uint256> typeReference = new TypeReference<Uint256>() {
//		};
//		outputParameters.add(typeReference);
//
//		String methodName = "balanceOf";
//		records.forEach(record -> {
//
//			CompletableFuture<Void> voidCompletableFuture = CompletableFuture.runAsync(() -> {
//
//				if (record.getMainNetwork().equals(WalletConstant.ETH)) {
//
//					List<Type> inputParameters = new ArrayList<>();
//
//					Address userAddress = new Address(record.getAddress());
//					inputParameters.add(userAddress);
//
//					Function function = new Function(methodName, inputParameters, outputParameters);
//
//					String encode = FunctionEncoder.encode(function);
//
//					Request<?, EthCall> ethCallRequest = web3j.ethCall(
//							Transaction.createEthCallTransaction(
//									record.getAddress(),
//									ethCurrency.getSmartContractAddress(),
//									encode
//							),
//							DefaultBlockParameterName.LATEST);
//					EthCall send;
//					try {
//						send = ethCallRequest.send();
//					} catch (IOException e) {
//						throw new RuntimeException(e);
//					}
//					String valueStr = new BigInteger(send.getValue().substring(2), 16).toString();
//					String balance = NumberUtil.toBigDecimal(valueStr).movePointLeft(Integer.parseInt(ethCurrency.getAccuracy())).stripTrailingZeros().toPlainString();
//					record.setBalance(balance);
//
//				} else {
//
//					String decryptStr = aes.decryptStr(record.getPrivateKey());
//					//				ApiWrapper wrapper = ApiWrapper.ofMainnet(decryptStr, currency.getApiKey());
//					ApiWrapper wrapper = ApiWrapper.ofNile(decryptStr);
//					Contract contract = wrapper.getContract(tronCurrency.getSmartContractAddress());
//					Trc20Contract trc20Contract = new Trc20Contract(contract, record.getAddress(), wrapper);
//					BigInteger bigInteger = trc20Contract.balanceOf(record.getAddress());
//					String balance = NumberUtil.toBigDecimal(String.valueOf(bigInteger)).movePointLeft(Integer.parseInt(tronCurrency.getAccuracy())).stripTrailingZeros().toPlainString();
//					record.setBalance(balance);
//				}
//			}, accountListThreadPoolExecutor);
//
//			completableFutureList.add(voidCompletableFuture);
//		});

	}


	/**
	 * 通过id查询账户
	 *
	 * @param id id
	 * @return R
	 */
	@ApiOperation(value = "通过id查询", notes = "通过id查询")
	@GetMapping("/{id}")
	@PreAuthorize("@pms.hasPermission('wallet_account_view')")
	public R<Account> getById(@PathVariable("id") Long id) {
		return R.ok(accountService.getById(id));
	}

	/**
	 * 新增账户
	 *
	 * @param account 账户
	 * @return R
	 */
	@ApiOperation(value = "新增账户", notes = "新增账户")
	@SysLog("新增账户")
	@PostMapping
	@PreAuthorize("@pms.hasPermission('wallet_account_add')")
	public R<Boolean> save(@RequestBody Account account) {
		return R.ok(accountService.save(account));
	}

	/**
	 * 修改账户
	 *
	 * @param account 账户
	 * @return R
	 */
	@ApiOperation(value = "修改账户", notes = "修改账户")
	@SysLog("修改账户")
	@PutMapping
	@PreAuthorize("@pms.hasPermission('wallet_account_edit')")
	public R<Boolean> updateById(@RequestBody Account account) {
		return R.ok(accountService.updateById(account));
	}

	/**
	 * 通过id删除账户
	 *
	 * @param id id
	 * @return R
	 */
	@ApiOperation(value = "通过id删除账户", notes = "通过id删除账户")
	@SysLog("通过id删除账户")
	@DeleteMapping("/{id}")
	@PreAuthorize("@pms.hasPermission('wallet_account_del')")
	public R<Boolean> removeById(@PathVariable Long id) {
		return R.ok(accountService.removeById(id));
	}


	/**
	 * 导出excel 表格
	 *
	 * @param account 查询条件
	 * @return excel 文件流
	 */
	@ResponseExcel
	@GetMapping("/export")
	@PreAuthorize("@pms.hasPermission('wallet_account_export')")
	public List<Account> export(Account account) {
		return accountService.list(Wrappers.query(account));
	}


	/**
	 * 生成账户
	 */
	@Inner
	@ApiOperation(value = "生成账户", notes = "生成账户")
	@GetMapping("/create/{userId}")
	@Transactional(rollbackFor = Exception.class)
	public R<Boolean> create(@PathVariable Long userId) {

		// 账户（以太坊）
		ECKeyPair ecKeyPair = null;
		try {
			ecKeyPair = Keys.createEcKeyPair();
		} catch (InvalidAlgorithmParameterException | NoSuchAlgorithmException | NoSuchProviderException e) {
			throw new RuntimeException(e);
		}
		// 公钥
		String ethPublicKey = ecKeyPair.getPublicKey().toString(16);
		// 私钥
		String ethPrivateKey = ecKeyPair.getPrivateKey().toString(16);
		// 地址
		String ethAddress = "0x" + Keys.getAddress(ethPublicKey);

		String ethAesPrivateKey = aes.encryptBase64(ethPrivateKey, Charset.defaultCharset());

		Account ethAccountSave = new Account();
		// 地址
		ethAccountSave.setAddress(ethAddress);
		// 主网
		ethAccountSave.setMainNetwork(WalletConstant.ETH);
		// 私钥
		ethAccountSave.setPrivateKey(ethAesPrivateKey);
		// 用户ID
		ethAccountSave.setUserId(userId);
//		ethAccountSave.setCurrency("1");
		accountService.save(ethAccountSave);

		// 账户（波场）
		KeyPair keyPair = KeyPair.generate();

		// String private key
		String tronPrivateKey = keyPair.toPrivateKey();

		// String public key
		String tronPublicKey = keyPair.toPublicKey();

		String tronAddress = keyPair.toBase58CheckAddress();

		String tronAesPrivateKey = aes.encryptBase64(tronPrivateKey, Charset.defaultCharset());

		Account tronAccountSave = new Account();
		tronAccountSave.setAddress(tronAddress);
		tronAccountSave.setMainNetwork(WalletConstant.TRON);
		tronAccountSave.setPrivateKey(tronAesPrivateKey);
		tronAccountSave.setUserId(userId);
//		tronAccountSave.setCurrency("1");
		accountService.save(tronAccountSave);

		// 资金账户
		CapitalAccount capitalAccount = new CapitalAccount();
		capitalAccount.setUserId(userId);
		capitalAccount.setAvailable(BigDecimal.ZERO);
		capitalAccount.setFrozen(BigDecimal.ZERO);
		capitalAccount.setCurrency(WalletConstant.USDT);
		capitalAccountService.save(capitalAccount);

		// 合约账户
		ContractAccount contractAccount = new ContractAccount();
		contractAccount.setFrozen(BigDecimal.ZERO);
		contractAccount.setMarginBalance(BigDecimal.valueOf(10000));
		contractAccount.setWalletBalance(BigDecimal.valueOf(10000));
		contractAccount.setUnrealizedProfitLoss(BigDecimal.ZERO);
		contractAccount.setCurrency(WalletConstant.USDT);
		contractAccount.setUserId(userId);
		contractAccountService.save(contractAccount);

		return R.ok();
	}

}
